<template>

  <h1>App</h1>
  <!-- 
    小总结：
          一些小技巧：  
                    1、数据存放的位置：   单个组件使用，就放到单个组件中； 
                                        多个组件使用，就存放到他们共同的父级组件中。
                    2、数组的增删改查：   数据的增删改查，要在数据存放的组件中进行，大多在app组件中。
                    3、props可以传属性，也可以传方法。可以将方法写到app组件中，然后再其他组件中调用该方法函数，也算是在app组件中进行数据处理。
                    4、用好 计算属性computed 和 watch 。
   -->
  <div class="common-layout">
    <el-container>
      <el-header>
        <Header @add="add"></Header>
      </el-header>
      <el-main>
        <Main :todolist="todolist" :del="del" :changeFlag="changeFlag"></Main>
      </el-main>
      <el-footer>
        <Footer :allNum="todolist.length" :selectNum="selectNum" :setAllCheck="setAllCheck" @changeAllFlag="changeAllFlag" :filterTodolist="filterTodolist">
        </Footer>
      </el-footer>
    </el-container>
  </div>

</template>

<script lang="ts">
import { defineComponent, ref, reactive, watch } from 'vue'
//自动引入--局部注册
import { ElContainer, ElHeader, ElMain, ElFooter } from 'element-plus'
//引入组件
import Header from './components/Header.vue'
import Main from './components/Main.vue'
import Footer from './components/Footer.vue'

import { todo } from './types/todo'

export default defineComponent({
  components: { ElContainer, ElHeader, ElMain, ElFooter, Header, Main, Footer },

  setup() {
    let todolist = reactive<todo[]>([
      {
        id: 0,
        tittle: '吃饭',
        flag: false
      },
      {
        id: 1,
        tittle: '睡觉',
        flag: false
      },
      {
        id: 2,
        tittle: '打豆豆',
        flag: false
      }
    ])

    //一、添加元素
    const add = (item: string) => {
      todolist.push({
        id: todolist.length,
        tittle: item,
        flag: false
      })
    }
    //二、删除元素 -- 数组删除元素 splice
    //思路：通过下标，删除数组元素。哪里有下标，在item组件中有，所以将删除元素的方法从app组件通过props一路传值到item组件。在item组件中，触发删除方法del，实现删除效果
    const del = (index: number) => {
      todolist.splice(index, 1)
      // console.log(todolist)
    }
    //三、修改多选框的flag状态
    //思路：想要更改多选框的状态，实现多选框 选中和没选中的切换；
    //但是  在item组件中不能修改，因为props传值是单向数据流，不能修改，所以采用计算属性的方式；
    //      在app组件中设置修改flag状态的方法，需要todo对象，和flag的状态两个参数， 通过props传值，将方法传下去，在item组件中，调用该方法，进行传参
    const changeFlag = (todo: todo, flag: boolean) => {
      todo.flag = flag
    }

    //四、设置信息总条数  
    //思路：设置总页数allName初始值，将 数组todolist的长度 通过props传值 给footer组件
    let allNum = ref(0)

    //五、实现选中多少条 用reduce
    //思路：  看数组todolist中 todo对象中 有几个的flag为true
    const selectNum = ref<number>(0)
    //通过watch 监听todolist的变化 因为 flag 的状态 是变化的。
    watch(todolist, (value) => {
      console.log(value)
      //使用数组的方法reduce
      //reduce()  将数组中的元素依次在函数中执行一遍
      //reduce(参数一：回调函数，参数二：回调函数的初始值)
      //reduce((参数一：pre-->回调函数的初始值或着上次一回调函数的返回值；参数二：item-->数组中的每一个元素；参数三：index-->当前执行元素的下标；参数四：arr-->当前执行的数组)=>{},初始值)
      selectNum.value = value.reduce((pre, item) => {
        return item.flag == true ? pre += 1 : pre
      }, 0)
      console.log(selectNum.value)
    }, {
      deep: true,
      immediate: true
    })

    //六、设置全选 全不选

    //方法一：有一个弊端，当所有的多选框 选中的时候，总选框并不会自动选中，自动化不高

    //思路：1、全选全不选，是由 全选框的flag状态所决定的，可以通过watch监听全选框，flag状态的变化，并通过emit将flag传给app组件。
    //2、在app组件中，通过数组遍历，然后，将子组件传回来的flag状态再赋值给数组中的各个元素。
    // const setAllCheck = (flag: boolean) => {//形参flag是子组件传过来的
    //   //遍历数组todolist，将数组中的所有todo对象中的flag 的状态 集体切换
    //   todolist.forEach((item) => {
    //     item.flag = flag
    //   })
    // }

    //方法二：
    //思路：    1、实现自动化，所有的多选框flag为true的时候，全选框自动为true。   在footer组件中 ，用计算属性 收集 全选框的 状态    get return的值 是由app组件传过去的，若所有的todo对象的flag都是true，则 全选框 为true，有一个不为true，则全选框 都为false
    //          2、全选框的flag状态 赋值给 所有的 多选框。  在计算属性的set函数中，将value传给app组件，在app组件中进行遍历，将所有的todo对象的flag 都赋值为 全选框的flag的值
    
    //(一)该回调函数 将多个多选框得flag状态传给footer组件
    const setAllCheck = ()=>{
      //当所有todo对象的flag都是true的时候，传过去一个true，只要有一个不为true，就传过去一个false
      // 当选中的条数selectNum与数组的长度todolist.length相等时，就是全选了
      return selectNum.value == todolist.length && todolist.length != 0 ? true : false
      //为了避免 当所有的信息删除之后 全选框依旧显示选中，所以给他加了一个条件 且当 数组todolist的长度为不0 时，全选框的flag为true
    }
    //(二)当全选框的flag发生变化时，将其变化的值传回来，再赋值给所有的todo对象
    const changeAllFlag = (value:boolean)=>{
      todolist.forEach((item)=>{
        item.flag = value
      })
    }


    //七、实现 所选删除

    //方法一：
    //思路： 根据todo对象中的flag状态，来判断todo对象，是否删除。
    //通过遍历 查看状态，返回新数组，进行渲染
    //需要将方法传过去  在子组件中触发
    // const selectDel = () => {
    //   //因为数组是根据下标进行删除的，数组的下标是动态的(当前一个元素根据下标删除后，后面的元素的下标都会发生变化，前移。导致删除的时候 连着的几个偶函数位是删不干净的)
    //   //所以使用for循环 倒着删除
    //   for(var i=todolist.length-1;i>=0;i--){
    //     if(todolist[i].flag){
    //       del(i)
    //     }
    //   }
    // }

    //方法二：   
    //思路：就是使用filter筛选出一个新的数组，数组中的todo对象的flag都是false状态，清空todolist，然后将新数组 赋值给todolist。
    const filterTodolist = ()=>{
      //筛选新数组
      let arr = todolist.filter((item)=>{
        return item.flag != true
      })
      //清空todolist
      // todolist = null //报错
      // todolist = []  //清不掉
      todolist.length =0
      //将新数组赋值给 todolist
      // todolist = arr //没效果
      arr.forEach((item)=>{
        todolist.push(item)
      })
    }



    return {
      todolist,
      add,
      del,
      changeFlag,
      allNum,
      selectNum,
      setAllCheck,
      changeAllFlag,
      // selectDel
      filterTodolist
    }
  }
})
</script>

<style scoped lang="scss">
div {
  .common-layout {
    border: 1px solid red;

    .el-header,
    .el-footer {
      height: 70px;
      background-color: #C6E2FF;
    }

    .el-main {
      background-color: #ECF5FF;
    }
  }
}
</style>